#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>

struct cdev cdev;
dev_t devno;

#define MAX_BUFF 1024
static char buff_registers[1024];

/* 文件打开函数 */
int dev_open(struct inode *inode, struct file *filp)
{
    /*获取次设备号*/
    int majorNum = MAJOR(inode->i_rdev);
    int minorNum = MINOR(inode->i_rdev);

#if 1
    if (minorNum == 0) {
        filp->private_data = buff_registers;
    }
    else if(minorNum == 1) {
        filp->private_data = buff_registers;
    }
    else {
        return -ENODEV;  //无效的次设备号
    }
#endif

    printk(KERN_ALERT "MMG_MemDev Open File %d:%d!\n", majorNum, minorNum);
    return 0;
}

/*文件释放函数*/
int dev_release(struct inode *inode, struct file *filp)
{
    printk(KERN_ALERT "MMG_MemDev Module Release!\n");
    return 0;
}

/*读函数*/
static ssize_t dev_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    printk(KERN_ALERT "MemDev Module Read!\n");
    unsigned long p = *ppos;
    unsigned int count = size;
    int ret = 0;
    int *register_addr = filp->private_data; /*获取设备的寄存器基地址*/

    /*判断读位置是否有效*/
    if (p >= MAX_BUFF ){
        return 0;
    }
    if (count > MAX_BUFF - p){
        count = MAX_BUFF - p;
    }

  /*读数据到用户空间*/
    if (copy_to_user(buf, register_addr + p, count))
    {
        ret = -EFAULT;
    }
    else
    {
        *ppos += count;
        ret = count;
    }

    return ret;
}

/*写函数*/

                            // 文件结构体指针, 用户空间缓冲区指针,                 写入数据的长度, 偏移量
static ssize_t dev_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
    printk(KERN_ALERT "MMG_MemDev File Write:%d %s!\n", strlen(buf), buf);
    unsigned long p =  *ppos;
    unsigned int count = size;
    int ret = 0;
    int *register_addr = filp->private_data; /*获取设备的寄存器地址*/

    /*分析和获取有效的写长度*/
    // if (p >= 5*sizeof(int)) return 0;

    if (p >= MAX_BUFF) {
        // 偏移量 > 最大缓冲区, 无法继续写入
        return 0;
    }

    // 写入数据长度 > 缓冲区剩余空间 时，只写入部分数据
    if (count > MAX_BUFF - p){
        count = MAX_BUFF - p;
    }

    /*从用户空间写入数据*/
    if (copy_from_user(register_addr + p, buf, count)) {
        ret = -EFAULT;
    } else {
        // 偏移量增加新增数据后的长度
        *ppos += count;
        ret = count;
    }

    // 返回写入的实际长度

    return ret;
}

/* seek文件定位函数 */
// 文件句柄, 偏移量, 移动方式 SEEK_SET SEEK_CUR SEEK_END
static loff_t dev_llseek(struct file *filp, loff_t offset, int whence)
{
    loff_t newpos;

    switch(whence) {
        case SEEK_SET:
            newpos = offset;
            break;

        case SEEK_CUR:
            newpos = filp->f_pos + offset;
            break;

        case SEEK_END:
            newpos = MAX_BUFF - 1 + offset;
            break;

        default:
            return -EINVAL;
    }

    if ((newpos < 0) || (newpos > MAX_BUFF))
        return -EINVAL;

    filp->f_pos = newpos;

    return newpos;
}

/*文件操作结构体*/
static const struct file_operations mem_fops =
{
    .open = dev_open,
    .read = dev_read,
    .write = dev_write,
    .llseek = dev_llseek,
    .release = dev_release,
};

/*设备驱动模块加载函数*/
static int dev_init(void)
{
    printk(KERN_ALERT "DDMMG: MemDev Module Initialize!\n");
    /*初始化cdev结构*/
    cdev_init(&cdev, &mem_fops);

    /* 注册字符设备 */
    // 初始化设备号, 0 主设备号起始位置, 数量, 主设备名称
    alloc_chrdev_region(&devno, 0, 2, "mmgdev");

    // 向内核注册字符设备
    cdev_add(&cdev, devno, 2);
    return 0;
}

/*模块卸载函数*/
static void dev_exit(void)
{
    printk(KERN_ALERT "DDMMG: MemDev Module UnInitialize!\n");
    cdev_del(&cdev);   /*注销设备*/
    unregister_chrdev_region(devno, 2); /*释放设备号*/
}

MODULE_LICENSE("GPL");

module_init(dev_init);
module_exit(dev_exit);

